package id.co.rho.scoring.cid.server.service.impl;

import id.co.rho.scoring.cid.domain.Criteria;
import id.co.rho.scoring.cid.domain.CriteriaType;
import id.co.rho.scoring.cid.domain.Model;
import id.co.rho.scoring.cid.server.dao.CriteriaDao;
import id.co.rho.scoring.cid.server.dao.GenericDao;
import id.co.rho.scoring.cid.server.service.UploadModelService;

import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class UploadModelServiceImpl implements UploadModelService {

	private GenericDao genericDao;
	private CriteriaDao criteriaDao;
	private FormulaEvaluator evaluator;
	private Model model;

	@Autowired
	public void setGenericDao(GenericDao genericDao) {
		this.genericDao = genericDao;
	}

	@Autowired
	public void setCriteriaDao(CriteriaDao criteriaDao) {
		this.criteriaDao = criteriaDao;
	}

	@Override
	public Boolean uploadModelExcel(InputStream inputStream, String modelName,
			String typeModelId) {
		Boolean response = false;
		Boolean valid = true;
		HSSFWorkbook workbook = null;
		try {
			workbook = new HSSFWorkbook(inputStream);
		} catch (IOException e) {
			valid = false;
		}
		evaluator = workbook.getCreationHelper().createFormulaEvaluator();
		if (criteriaDao.findModelByName(modelName) > 0) {
			valid = false;
		}

		if (valid) {
			model = new Model();
			model.setRevision(1);
			model.setModelName(modelName);
			model.setCreatedDate(new Date());
			model.setTypeModelConfig(typeModelId);
			model.setAktifStatus(Boolean.FALSE);

			for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
				HSSFSheet sheet = workbook.getSheetAt(i);
				Criteria criteria = new Criteria();
				criteria.setCriteriaName(sheet.getSheetName());
				criteria.setCriteriaWeight(sheet.getRow(0).getCell(1) != null ? Double
						.parseDouble(sheet.getRow(0).getCell(1).toString()) : 0);
				criteria.setLocation(i);
				int numberOfCriteria = sheet.getRow(1)
						.getPhysicalNumberOfCells();
				Map<Integer, Criteria> latestCriteria = new HashMap<Integer, Criteria>();
				if (numberOfCriteria % 5 == 0) {
					for (int j = 2; j < sheet.getLastRowNum(); j++) {
						HSSFRow row = sheet.getRow(j);
						for (int k = 0; k < numberOfCriteria; k = k + 5) {
							if (cellEval(row.getCell(k)) != null) {
								if (!row.getCell(k).toString().trim().isEmpty()) {
									Criteria criteria2 = new Criteria();
									criteria2.setCriteriaNumber(row.getCell(k)
											.toString());
									criteria2.setCriteriaName(cellEval(row
											.getCell(k + 1)) != null ? row
											.getCell(k + 1).toString() : null);

									String criteriaType = cellEval(row
											.getCell(k + 2)) != null ? row
											.getCell(k + 2).toString() : null;

									if (criteriaType != null) {
										if (CriteriaType.QUESTION.toString()
												.equalsIgnoreCase(criteriaType)) {
											criteria2
													.setCriteriaType(CriteriaType.QUESTION);
										} else if (CriteriaType.ANSWER
												.toString().equalsIgnoreCase(
														criteriaType)) {
											criteria2
													.setCriteriaType(CriteriaType.ANSWER);
										} else {
											criteria2
													.setCriteriaType(CriteriaType.CRITERIA);
										}
									}

									criteria2.setCriteriaRank(cellEval(row
											.getCell(k + 3)) != null ? Double
											.parseDouble(row.getCell(k + 3)
													.toString()) : null);

									criteria2.setCriteriaWeight(cellEval(row
											.getCell(k + 4)) != null ? Double
											.parseDouble(row.getCell(k + 4)
													.toString()) : null);

									if (k != 0) {
										criteria2
												.setLocation(latestCriteria
														.get(k - 5).getChild()
														.size() == 0 ? 0
														: this.getLatestPosition(latestCriteria
																.get(k - 5)
																.getChild()) + 1);
										criteria2.setParent(latestCriteria
												.get(k - 5));
										latestCriteria.get(k - 5).getChild()
												.add(criteria2);
									} else {

										criteria2
												.setLocation(criteria
														.getChild().size() == 0 ? 0
														: this.getLatestPosition(criteria
																.getChild()) + 1);
										criteria2.setParent(criteria);
										criteria.getChild().add(criteria2);
									}
									latestCriteria.put(k, criteria2);
								}
							}

						}
					}
				}
				model.getCriteries().add(criteria);
			}

			response = true;
		}

		return response;

	}

	@Override
	public void saveModel() {
		this.genericDao.saveOrUpdate(this.model);

	}

	public Object cellEval(Cell cell) {
		Object obj = null;
		if (cell != null) {
			switch (evaluator.evaluateFormulaCell(cell)) {
			case Cell.CELL_TYPE_BOOLEAN:
				obj = cell.getBooleanCellValue();
				break;
			case Cell.CELL_TYPE_NUMERIC:
				if (DateUtil.isCellDateFormatted(cell)) {
					obj = cell.getDateCellValue();
				} else {
					DecimalFormat myFormatter = new DecimalFormat(".0000");
					obj = myFormatter.format(cell.getNumericCellValue());
				}
				break;
			case Cell.CELL_TYPE_STRING:
				obj = cell.getStringCellValue();
				break;
			case Cell.CELL_TYPE_ERROR:
				obj = cell.getErrorCellValue();
				break;
			case Cell.CELL_TYPE_FORMULA:
				obj = evaluator.evaluateFormulaCell(cell);
				break;
			default:
				obj = cell;
				break;
			}
		}
		return obj;
	}

	public Integer getLatestPosition(List<Criteria> children) {
		Integer result = 0;
		for (Criteria criteria : children) {
			if (criteria.getLocation() > result) {
				result = criteria.getLocation();
			}
		}
		return result;
	}

	public Model getModel() {
		return model;
	}

	public void setModel(Model model) {
		this.model = model;
	}

}
